from django.db.models import Q, Sum, Subquery, OuterRef
from drf_yasg import openapi
from drf_yasg.utils import swagger_auto_schema, logger
from rest_framework import status, views
from rest_framework.decorators import action
from rest_framework.response import Response

from BYD_ERP.utils.base_serializer import ChoiceGoodsSearchSerializer
from BYD_ERP.utils.cont import NumberPrefix
from BYD_ERP.utils.generate_code import generate_code
from rest_framework import generics

from BYD_ERP.utils.pagination import GlobalPagination
from goods_info.models import GoodsInventoryModel, GoodsModel
from erp_system.models import UserModel


class MultipleAuditMixin:
    """
        定义批量审核（或者反审核）的视图函数
    """

    body_json = openapi.Schema(type=openapi.TYPE_OBJECT, required=['ids', 'user_id', 'is_audit'], properties={
        'ids': openapi.Schema(type=openapi.TYPE_ARRAY, items=openapi.Schema(type=openapi.TYPE_INTEGER),
                              description="选择那些需要审核/反审核的ID（主键）列表"),
        'user_id': openapi.Schema(type=openapi.TYPE_INTEGER, description="审核人/审核用户的ID"),
        'is_audit': openapi.Schema(type=openapi.TYPE_STRING, description="是否审核，审核：1，反审核：0"),
    })

    @swagger_auto_schema(method='put', request_body=body_json, operation_description="批量审核/反审核")
    @action(methods=['put'], detail=False)
    def multiple_audit(self, request, *args, **kwargs):
        audit_ids = request.data.get('ids')
        user_id = request.data.get('user_id')
        is_audit = request.data.get('is_audit', '1')
        if not audit_ids:
            return Response(data={'detail': '参数错误，ids为必传参数'}, status=status.HTTP_400_BAD_REQUEST)
        if not isinstance(audit_ids, list):
            # 如果传过来的参数不是列表
            return Response(data={'detail': '参数错误，ids必须是列表类型'}, status=status.HTTP_400_BAD_REQUEST)

        queryset = self.get_queryset()
        # self就是当前这个对象
        aud_queryset = queryset.filter(id__in=audit_ids)
        if aud_queryset.count() != len(audit_ids):
            return Response(data={'detail': '审核的数据不存在'}, status=status.HTTP_400_BAD_REQUEST)

        # 加上all()才能变成列表
        for item in aud_queryset.all():    # item代表一个单据（采购订单）
            if item.status != '1' and is_audit == '0':
                # 采购订单状态只有为1的时候才能进行反审核操作
                return Response(data={'detail': '不能进行反审核操作，因为当前单据是未审核状态或已经生效'}, status=status.HTTP_400_BAD_REQUEST)

            if item.status != '0' and is_audit == '1':
                # 采购订单状态只有为0的时候才能进行审核操作
                return Response(data={'detail': '不能进行审核操作，因为当前单据已经审核过'}, status=status.HTTP_400_BAD_REQUEST)

        # 用户的ID
        if not user_id:
            return Response(data={'detail': '参数错误，user_id为必传参数'}, status=status.HTTP_400_BAD_REQUEST)
        check_user = UserModel.objects.get(id=int(user_id))

        aud_queryset.update(status=is_audit, check_user_name=check_user.real_name, check_user_id=check_user.id)

        return Response(status=status.HTTP_200_OK)
        pass


class MultipleDestroyMixin:
    """
    定义批量删除的视图函数
    """

    del_ids = openapi.Schema(type=openapi.TYPE_OBJECT, required=['ids'], properties={
        'ids': openapi.Schema(type=openapi.TYPE_ARRAY, items=openapi.Schema(type=openapi.TYPE_INTEGER),
                              description="选择那些需要删除的ID（主键）列表")
    })

    @swagger_auto_schema(method='delete', request_body=del_ids, operation_description="批量删除")
    @action(methods=['delete'], detail=False)
    def multiple_delete(self, request, *args, **kwargs):
        delete_ids = request.data.get('ids')
        if not delete_ids:
            return Response(data={'detail': '参数错误，ids为必传参数'}, status=status.HTTP_400_BAD_REQUEST)
        if not isinstance(delete_ids, list):
            # 如果传过来的参数不是列表
            return Response(data={'detail': '参数错误，ids必须是列表类型'}, status=status.HTTP_400_BAD_REQUEST)

        queryset = self.get_queryset()
        # self就是当前这个对象
        del_queryset = queryset.filter(id__in=delete_ids)
        if del_queryset.count() != len(delete_ids):
            return Response(data={'detail': '删除的数据不存在'}, status=status.HTTP_400_BAD_REQUEST)

        del_queryset.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)
        pass


# 批量启用：
class MultipleOpenMixin:
    """
    定义批量启用或者禁用的视图函数
    """

    open_ids = openapi.Schema(type=openapi.TYPE_OBJECT, required=['ids'], properties={
        'ids': openapi.Schema(type=openapi.TYPE_ARRAY, items=openapi.Schema(type=openapi.TYPE_INTEGER),
                              description="选择那些需要批量启用或者禁用的ID（主键）列表"),
        'is_open': openapi.Schema(type=openapi.TYPE_STRING, description="是否启用，启用是：0，禁用是：1"),
    })

    @swagger_auto_schema(method='delete', request_body=open_ids, operation_description="批量启用或者禁用")
    @action(methods=['delete'], detail=False)
    def multiple_open(self, request, *args, **kwargs):
        open_ids = request.data.get('ids')
        is_open = request.data.get('is_open', '0')  # 这里的“0”代表给定了个默认值是0
        if not open_ids:
            return Response(data={'detail': '参数错误，ids为必传参数'}, status=status.HTTP_400_BAD_REQUEST)
        if not isinstance(open_ids, list):
            # 如果传过来的参数不是列表
            return Response(data={'detail': '参数错误，ids必须是列表类型'}, status=status.HTTP_400_BAD_REQUEST)

        queryset = self.get_queryset()
        # self就是当前这个对象
        open_queryset = queryset.filter(id__in=open_ids)
        if open_queryset.count() != len(open_ids):
            return Response(data={'detail': '启用或禁用的数据不存在'}, status=status.HTTP_400_BAD_REQUEST)

        open_queryset.update(delete_flag=is_open)
        return Response(status=status.HTTP_200_OK)
        pass


# 生成各种编号的接口
class GenerateCode(views.APIView):
    prefix_param = openapi.Parameter(name='prefix', in_=openapi.IN_QUERY, description="编号的前缀，可以参考cont.py",
                                     type=openapi.TYPE_STRING)

    @swagger_auto_schema(manual_parameters=[prefix_param], operation_description="自动生成各种编号（流水号）")
    def get(self, request, *args, **kwargs):
        """
        生成各种编号的接口，必须传一个前缀：/api/generate_code/prefix=ord，可以参考cont.py

        返回一个28位的编号字符串，return：code就是生成的编号
        """
        prefix = request.query_params.get('prefix', None)
        if prefix:
            if prefix in NumberPrefix.__members__:
                code = generate_code(NumberPrefix[prefix].value)
                return Response(data={'code': code}, status=status.HTTP_200_OK)
            else:
                return Response(data={'detail': 'prefix没有配置，参考cont.py'}, status=status.HTTP_400_BAD_REQUEST)
        else:
            return Response(data={'detail': 'prefix没有，该参数必传'}, status=status.HTTP_400_BAD_REQUEST)


# 选择商品
class ChoiceGoodsSearchView(generics.GenericAPIView):
    serializer_class = ChoiceGoodsSearchSerializer
    pagination_class = GlobalPagination

    # 分页参数必须是query_param
    page_param = openapi.Parameter(name='page', in_=openapi.IN_QUERY, description="页号", type=openapi.TYPE_STRING)
    size_param = openapi.Parameter(name='page_size_user_defined', in_=openapi.IN_QUERY, description="每页显示的数量",
                                   type=openapi.TYPE_STRING)

    @swagger_auto_schema(request_body=ChoiceGoodsSearchSerializer, manual_parameters=[page_param, size_param],
                         operation_description="搜索需要选择的货品列表")
    def post(self, request, *args, **kwargs):
        ser = ChoiceGoodsSearchSerializer(data=request.data)
        if ser.is_valid(raise_exception=True):
            keyword = ser.validated_data.get('keyword', None)
            partner = ser.validated_data.get('partner', None)
            warehouse_id = ser.validated_data.get('warehouse_id', 0)
            category_id = ser.validated_data.get('category_id', 0)

            query = Q()
            # 根据仓库来过滤查询，并且查询每个商品的库存
            # 这里是基于GoodsInventoryModel的查询
            if warehouse_id:
                inventory_list = GoodsInventoryModel.objects.filter(warehouse_id=warehouse_id).filter(
                    goods_id=OuterRef('id')).values(
                    'goods_id').annotate(total_sum=Sum('cur_inventory'))
                # 只是为了保证和下面的返回的结果是一致的格式，同样使用了Sum()
            else:  # 没有指定仓库，意味着：查询每个商品所有仓库的总库存和
                # values('goods_id')就是说根据goods_id来分组，这时返回的是字典
                # 下面返回的inventory_list，嵌套着字典的列表
                # 这里是联表查询的子查询
                inventory_list = GoodsInventoryModel.objects.filter(goods_id=OuterRef('id')).values(
                    'goods_id').annotate(total_sum=Sum('cur_inventory'))

            # 下面是基于GoodsModel的过滤查询
            if keyword:
                child_query = Q()
                child_query.add(Q(name__contains=keyword), 'OR')
                child_query.add(Q(specification__contains=keyword), 'OR')
                child_query.add(Q(model_number__contains=keyword), 'OR')
                query.add(child_query, 'AND')
            if partner:
                query.add(Q(partner__contains=partner), 'AND')
            if category_id:
                query.add(Q(category_id=category_id), 'AND')
            result = GoodsModel.objects.filter(query).values('id', 'name', 'specification', 'model_number', 'partner',
                                                             'units__basic_name', 'category__name').annotate(
                cur_inventory=Subquery(inventory_list.values('total_sum'))).all()
            # 下面是对返回的结果加上分页
            pg_result = self.paginate_queryset(result)
            # pg_result就是查询结果，借助序列化器进行序列化，序列化之后，对象变成字典，同时也能验证格式是否规范正确

            result_ser = self.get_serializer(instance=pg_result, many=True)
            return self.get_paginated_response(result_ser.data)

